<html>
  <head>
    <title>ChromiumIRC</title>
    <link rel="stylesheet" type="text/css" href="styles.css"> 
    <script src="jstemplate/util.js" type="text/javascript"></script>
    <script src="jstemplate/jsevalcontext.js" type="text/javascript"></script>
    <script src="jstemplate/jstemplate.js" type="text/javascript"></script> 
    <script src="util.js" type="text/javascript"></script>
    <script lang="JavaScript" src="irc.js"></script>
    <script>

var ircConnections = {};

// The server & channel configutation data is stored in localStorage.servers.
// These are setters and getters for this structure.
function servers() {
  return JSON.parse(localStorage.servers || "[]");
}
function setServers(servers) {
  localStorage.servers = JSON.stringify(servers);
}

// Channel list is a sorted list of "server#channel" strings. This maps to
// channel slides as represented in the UI.
function channelList() {
  var channelList = [];
  servers().forEach(function(server) {
    server.channels = server.channels || [];
    server.channels.forEach(function(channel) {
      channelList.push(server.name + channel);
    });
  });

  channelList.sort();
  return channelList;
}

window.onload = function() {
  // Setup notifications.
  window.onfocus = function() {
    windowHasFocus = true;
    clearNotifications();
  }
  window.onblur = function() {
    windowHasFocus = false;
  }

  syncChannelList();

  // Setup channel navigation and message entry.
  function handleBodyKeyDown(event) {
    switch (event.keyCode) {
      case 37: // left arrow
        slideTo(-1);
        break;
      case 39: // right arrow
        slideTo(1);
        break;
    }
  }
  document.body.addEventListener('keydown', handleBodyKeyDown, false);

  // We don't want left & right arrow inside the text entry to move the channel
  // slides.
  $('typingDiv').addEventListener('keydown', function(event) {
    event.stopPropagation();
  });
  $('entryText').addEventListener('keydown', function(event) {
    if (event.keyCode == 13) { // RETURN key.
      processEntryMessage();
    }
  });

  servers().forEach(addServerConnection);
};

window.onunload = function() {
  ircConnections.forEach(function(irc) {
    irc.disconnect();  
  });
}

function addServerConnection(server) {
  var ws = new WebSocket("ws://" + location.host + "/ws");
  var irc = new IRCConnection(server.name, server.port, server.nick,
                              ws.send.bind(ws), // sendFunc
                              ws.close.bind(ws)); // closeFunc
  ws.onopen = irc.onOpened.bind(irc);
  ws.onclose = irc.onClosed.bind(irc);
  ws.onmessage = function(message) {
    irc.onMessage(message.data);  
  };
  irc.onConnect = function(message) {
    server.channels.forEach(function(channel) {
      ircConnections[server.name].joinChannel(channel);
    });
  };
  irc.onDisconnect = function(message) {
  };
  irc.onText = function(channel, nick, message) {
    checkForNickReference(server, channel, nick, message);
    addMessage(server.name, channel, nick, new Date(), message);
  };

  ircConnections[server.name] = irc;
}

function joinChannel(serverName, channelName) {
  ircConnections[serverName].joinChannel(channelName);
}

function removeChannelListener(channelName) {
  return function(event) {
    event.stopPropagation();
    
    var servers = servers();
    servers.forEach(function(server) {
      if (channelName.indexOf(server.name) == 0) {
        for (var i = 0; server.channels.length; i++) {
          if (channelName == server.name + server.channels[i]) {
            ircConnections[server.name].quitChannel(server.channels[i]);
            server.channels.splice(i, 1);
            break;
          }
        }
      }
    });

    setServers(servers);
    syncChannelList();
  };
}

function syncChannelList() {
  var channels = channelList();
  var channelSlides = $('channelSlides');
  var channelSlideProto = $('channelSlideProto');

  var channelIndex = 0;
  var slideIndex = 0;

  while(channelIndex < channels.length || 
        channels.length != channelSlides.children.length) {
    var channel = channels[channelIndex];
    var slide = channelSlides.children[slideIndex];

    if (slideIndex == channelSlides.children.length ||
        channel < slideChannel(slide)) {
      // Add a new slide.
      var newSlide = channelSlideProto.cloneNode(true);
      jstProcess(new JsEvalContext({ name: channel }), newSlide);
      newSlide.setAttribute("id", "channel-" + channel);
      newSlide.style.display = "";
      if (slideIndex == channelSlides.children.length) {
        channelSlides.appendChild(newSlide);
      } else {
        channelSlides.insertBefore(newSlide, slide);
      }
      newSlide.addEventListener('click', onClickMoveSlide);
      childNodeWithClass(newSlide, "removeButton")
          .addEventListener('click', removeChannelListener(channel));
      
      slide = newSlide;
    } else if (!channel || channel > slideChannel(slide)) {
      // Delete a removed slide.
      
      // If the removed slide is the current slide, we have to pick a new
      // current slide.
      if (localStorage.currentSlide == slideChannel(slide)) {
        if (slide.nextSibling) {
          localStorage.currentSlide = slideChannel(slide.nextSibling);
        } else if (channels.length == 0) {
          localStorage.currentSlide = "";
        } else if (slideIndex < channelSlides.children.length) {
          localStorage.currentSlide =
              slideChannel(channelSlides.children[slideIndex - 1]);
        }
      }
      channelSlides.removeChild(slide);  
    } else {
      channelIndex++;
      slideIndex++;
    }

    slide.setAttribute("slide", "" + slideIndex - 1);
  }

  slideTo();
}

function processEntryMessage() {
  var message = $('entryText').value;
  $('entryText').value = "";

  if (!localStorage.currentSlide) {
    alert('No current channel');
    return;
  }
  
  var server;
  var channel;
  var nick;
  servers().forEach(function(s) {
    if (localStorage.currentSlide.indexOf(s.name) == 0) {
      server = s.name;
      nick = s.nick;
      s.channels.forEach(function(c) {
        if (localStorage.currentSlide == s.name + c) {
          channel = c;
        }
      });
    }
  });

  addMessage(server, channel, nick, new Date(), message);
  ircConnections[server].sendMessage([channel], message);
}

function addMessage(server, channel, nick, time, body) {
  messageLine = childNodeWithClass($('channelSlideProto'), "messageLine");
  var newMessageLine = messageLine.cloneNode(true);

  jstProcess(new JsEvalContext({ 
    'nick': nick,
    'time': time,
    'body': body
  }), newMessageLine);
  newMessageLine.style.display = "";

  var messageList =
      childNodeWithClass($("channel-" + server + channel), "messageList");
  messageList.appendChild(newMessageLine);
}

function formatTime(time) {
  return "";
}

/**
 * Slide Navigation. 
 */
 
// Returns the server#channel string value for a given |slide| element.
function slideChannel(slide) {
  return childNodeWithClass(slide, "channel").innerText;
}

// Handler for clicking on the visible portions of the previous & next slides.
function onClickMoveSlide() {
  if (localStorage.currentSlide != slideChannel(this)) {
    localStorage.currentSlide = slideChannel(this);
    slideTo();
  }  
}

// Handles navigating between the channel slides. If |slideDelta| is given,
// it should specify the number of slides to move left (negative value) or right
// positive value. If |slideDelta| is not provided, It ensures that
// |localStorage.currentSlide| is navigated to.
function slideTo(slideDelta) {
  var slide;
  var slideNumber;

  if (localStorage.currentSlide) {
    slide = document.getElementById("channel-" + localStorage.currentSlide);
    if (slide) {
      slideNumber = parseInt(slide.getAttribute("slide"));
    }
  }
  if (isNaN(slideNumber) || !slide) {
    slideNumber = 0;
  }
  if (typeof(slideDelta) == "number") {
    slideNumber += slideDelta;
  }

  var slides = document.getElementsByClassName("channelSlide");
  if (slideNumber < 0 || slideNumber == slides.length - 1) {
    return;
  }

  for (var i = 0; i < slides.length; i++) {
    var slide = slides[i];
    var slideIndex = parseInt(slide.getAttribute("slide")) - slideNumber;
    
    if (slideIndex <= -2) {
      slide.className = "channelSlide far-left";
    }
    if (slideIndex >= 2) {
      slide.className = "channelSlide far-right";
    }
    
    switch(slideIndex) {
      case -1:
        slide.className = "channelSlide left";
        break;
      case 0:
        slide.className = "channelSlide center";
        localStorage.currentSlide = slideChannel(slide);
        break;
      case 1:
        slide.className = "channelSlide right";
        break;
    }
  }

  clearNotifications();
}

/**
 * Notifications
 */
var windowHasFocus = false;
var notifications = {};

function clearNotifications() {
  for (property in notifications) {
    notifications[property].cancel();
  }

  notifications = {};
}

function checkForNickReference(server, channel, nick, message) {
  if (windowHasFocus || !message || message.indexOf(server.nick) < 0) {
    return;
  }

  // Notifications will be enabled by the app install. Otherwise, don't notity.
  if (Notification.permission != "granted") {
    return;
  }

  // Remove a previous notification from the same channel. Show the newer one.
  if (notifications[server.name + channel]) {
    notifications[server.name + channel].close();
  }

  var n = new Notification("On " + server.name + channel, {
    icon: "https://www.google.com/favicon.ico",
    body: nick + ": " + message,
  });

  n.onshow = function() {};
  n.onclose = function() {
    delete notifications[server.name + channel];
  };

  notifications[server.name + channel] = n;
}
    </script>
  </head>
  <body>
    <!--  TEMPLATES -->
    <div id="channelSlideProto" style="display:none" class="channelSlide">
      <div class="channelControls">
        <div jscontent="name" class="channel">
        </div>
        <div class="removeButton">
          x
        </div>
      </div>
      <div class="channelSlideContainer">
        <div class="messageListContainer">
          <div class="messageList">
            <div jsselect="messages">
              <div class="messageLine">
                <div jscontent="nick" class="messageSender"></div>:
                <div jscontent="body" class="messageBody"></div>
              </div>
            </div>
          </div>
        </div>
        <div class="messageListSpacer">.</div>
      </div>
    </div>
    
    <div id="pageContainer">
      <div id="headerContainer">
        <div id="pageControls">
          <div onclick="window.open('addServer.html');">
            <div class="addControlLabel">
              add server
            </div>
            <div class="addButton">
              +
            </div>
          </div>
          <div onclick=" window.open('addChannel.html');">
            <div class="addControlLabel">
              add channel
            </div>
            <div class="addButton">
              +
            </div>
          </div>
        </div>
      </div>
      <div id="slideContainer">
        <div id="typingDiv">
          <input type="text" id="entryText" value="">
        </div>
        <div style="" id="channelSlides">
        </div>
      </div>
    </div>
  </body>
</html>
